package internal_map_hash

import (
	"math/rand"
	"reflect"
	"sync"
	"unsafe"
)

// 从原生的map中获取到它的hash函数
// 针对comparable类型,map都可以计算得到一个hash值,期望将这个特性转化为一个函数
// 方便外界使用.

type Hasher struct {
	mu   sync.RWMutex
	fm   map[unsafe.Pointer]hashFn
	seed uintptr
}

func NewHasher() *Hasher {
	return &Hasher{
		fm:   make(map[unsafe.Pointer]hashFn),
		seed: newHashSeed(),
	}
}

type hashFn func(unsafe.Pointer, uintptr) uintptr

type intfaceMark struct {
	typ   unsafe.Pointer
	value unsafe.Pointer
}

func (h *Hasher) Sum(data any) uint64 {
	im := (*intfaceMark)(unsafe.Pointer(&data))
	var hashFunc hashFn
	var ok bool
	h.mu.RLock()
	if hashFunc, ok = h.fm[im.typ]; ok {
		h.mu.RUnlock()
		p := noescape(im.value)
		return uint64(hashFunc(p, h.seed))
	}
	h.mu.RUnlock()

	// 使用反射创建一个map类型,然后得到map的地址,从地址的typ字段中获取到此类型用于hash的函数
	m := reflect.MapOf(reflect.TypeOf(data), reflect.TypeOf(true))
	mapValue := reflect.MakeMap(m) // 得到value代表map对象
	rv := *(*reflectValue)(unsafe.Pointer(&mapValue))

	hashFunc = (*maptype)(rv.typ).hasher
	h.mu.Lock()
	h.fm[im.typ] = hashFunc
	h.mu.Unlock()
	p := noescape(im.value)
	return uint64(hashFunc(p, h.seed))
}

type reflectValue struct {
	typ unsafe.Pointer
	val unsafe.Pointer
	f   uintptr
}

//go:nosplit
//go:nocheckptr
func noescape(p unsafe.Pointer) unsafe.Pointer {
	x := uintptr(p)
	return unsafe.Pointer(x ^ 0)
}

func newHashSeed() uintptr {
	return uintptr(rand.Int())
}
